/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package dcm4che;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import javax.imageio.ImageIO;
import libraries.FunctionLibrary;
import libraries.LanguageLibrary;
import libraries.PathLibrary;
import model.Consultation;
import model.DB2Hash;

import org.dcm4che2.data.BasicDicomObject;
import org.dcm4che2.data.DicomElement;
import org.dcm4che2.data.DicomObject;
import org.dcm4che2.data.UID;

import org.dcm4che2.data.Tag;
import org.dcm4che2.data.VR;
import org.dcm4che2.data.VRMap;
import org.dcm4che2.io.DicomOutputStream;
import org.dcm4che2.util.UIDUtils;

import org.dcm4che2.tool.jpg2dcm.Jpg2Dcm;
/**
 *
 * @author aeguzkiza
 */
public class JPG2dcm {
    Consultation consultation;
    PathLibrary PL = new PathLibrary();
    LanguageLibrary LL = new LanguageLibrary();
    FunctionLibrary LF;
    Jpg2Dcm jpg2dcm = new Jpg2Dcm();

    // Subsequences.
    DicomElement L1;
    DicomElement L2;
    ArrayList<String> CIDs;
    DicomObject dataset_L1; // 1rst level dataset.
    DicomObject dataset_L2; // 2nd level dataset.

    // Fixed attribute values
    String ImplementationVersionName = "DICAMP 1.0";
    String SpecificCharacterSet = "ISO_IR 100";
    String ImageType = "DERIVED\\SECONDARY";
    String DerivationDescription = "Imagen digitalizada a partir de un documento imprimido por el dispositivo original en papel. Al la salida de la impresora se ha colocado un escáner que digitaliza los documentos como imagen.";
    String ImageUID = UID.JPEGBaseline1;    // 1.2.840.10008.1.2.4.50

    String PlanarConfiguration = "0";   // ??? dicom.putString(Tag.PlanarConfiguration, VR.US, "1");
    Integer PixelRepresentation = 0;

    String LossyImageCompression = "00";   // Lossless compression.
    String SeriesNumber = "2";  // Value by default.
    String ValueType = "CODE";  // It will be predefined as code.

    String ManufacturersModelName = "DICAMP";
    String DeviceSerialNumber = "00001";
    String SoftwareVersions = "1.0";

    String BodyPartExamined = "EYE";
    String PatientPosition = "AP";
    String StudyTime = "000000.0";      // Should be present with at least a zero-length.
    String PatientOrientation = "";     // Should be present with at least a zero-length.
    String ImagesInAcquisition = "1";   // Should be present with at least a zero-length.

    public static class MyException extends Exception { // TESTING THIS POSSIBILITY.
        static final int CantReadFile = 1;
        static final int NoFile = 2;
        static final int SystemCallError = 3;
        static final int WrongLaterality = 4;
        
        private int type;
        private String string;

        public MyException(int type, String string) {
            super();
            this.type = type;
            this.string = string;
        }

        @Override
        public String getMessage() {
            switch (type) {
            case CantReadFile:
                    return "Archivo inválido: no se ha podido leer la imagen propuesta en la ruta:\n["+string+" ]";
            case NoFile:
                    return "No se ha encontrado el archivo correspondiente a la adquisición seleccionada en la ruta:\n["+string+" ]";
            case SystemCallError:
                    return "Es posible que la configuración del servidor no sea correcta, \npara resolver el problema revise la ventana de configuración.\n\nHa surgido un fallo de comunicación con el PACS al realizar la siguiente llamada cmd:\n["+string+" ]";
            case WrongLaterality:
                    return "Es posible que el valor de lateralidad no sea correcto, \n y por consiguiente algun atributo DICOM puede verse afectado. \nPara resolver el problema revise la adquisición.\n\nCuando debería ser 'R' o 'L' la lateralidad toma el siguiente valor:\n["+string+" ]";
            default:
                    return "Error desconocido: string = "+string+" type = "+type;
            }
        }
    }

    public JPG2dcm(DB2Hash physician, DB2Hash patient, DB2Hash study, DB2Hash acquisition, DB2Hash device, DB2Hash worklist) throws MyException, IOException {
        String imagePath = acquisition.get("ImagePath");
        //System.out.println("ImagePath: "+imagePath);
        File jpgSource = new File(imagePath);        
        String Laterality = acquisition.get("ImageLaterality");
        String acquisitionName = acquisition.get("AcquisitionName");
        String newFilePath = PL.getimgs2sendPath()+"\\"+acquisitionName+".dcm";
        File newFileDCM = new File(newFilePath);
        //System.out.println("newFileDCM: "+newFilePath);
        if(!newFileDCM.exists()){
            boolean success = newFileDCM.createNewFile();
            if (success) {System.out.println("new File CREATED in the next path: "+newFilePath);  // File did not exist and was created
            } else {System.out.println("the File already EXISTS in the next path: "+newFilePath);}   // File already exists
                      
        }

        //jpg2dcm.convert(jpgSource, newFileDCM);
        
        BufferedImage jpegImage = ImageIO.read(jpgSource);
        if (jpegImage == null){
            throw new MyException(MyException.SystemCallError, imagePath );
        }
        // Get infromation from jpg file:
        int colorComponents = jpegImage.getColorModel().getNumColorComponents();
        int bitsPerPixel = jpegImage.getColorModel().getPixelSize();
        int bitsAllocated = (bitsPerPixel / colorComponents);
        int samplesPerPixel = colorComponents;


        
        DicomObject dicom = new BasicDicomObject();

        // Meta Header:
        dicom.putString(Tag.SourceApplicationEntityTitle, VR.AE, device.get("AETitle"));
        dicom.putString(Tag.SpecificCharacterSet, VR.CS, SpecificCharacterSet);
        L1 = dicom.putSequence(Tag.DerivationCodeSequence);     // Repeated.
            dataset_L1 = new BasicDicomObject();
            dataset_L1.putString(Tag.CodingSchemeDesignator, VR.SH, "DCM");
            dataset_L1.putString(Tag.CodeValue, VR.SH, "113090");
            dataset_L1.putString(Tag.CodeMeaning, VR.LO, "Image stitching");
        L1.addDicomObject(dataset_L1);
        dicom.add(L1);
        dicom.putString(Tag.ImageType, VR.CS, ImageType);    // ???
        L1 = dicom.putSequence(Tag.DerivationImageSequence);
            dataset_L1 = new BasicDicomObject();
            dataset_L1.putString(Tag.DerivationDescription, VR.ST, DerivationDescription);
            L2 = dataset_L1.putSequence(Tag.DerivationCodeSequence);
                dataset_L2 = new BasicDicomObject();
                dataset_L2.putString(Tag.CodingSchemeDesignator, VR.SH, "DCM");
                dataset_L2.putString(Tag.CodeValue, VR.SH, "113090");
                dataset_L2.putString(Tag.CodeMeaning, VR.LO, "Image stitching");
            L2.addDicomObject(dataset_L2);
        L1.addDicomObject(dataset_L1);
        dicom.add(L1);
        dicom.putString(Tag.PhotometricInterpretation, VR.CS, samplesPerPixel == 3 ? "YBR_FULL_422" : "MONOCHROME2");   // If there are 3 samples per pixel the image has colors, else grayscale.

        dicom.putString(Tag.PlanarConfiguration, VR.US, PlanarConfiguration);

        dicom.putInt(Tag.SamplesPerPixel, VR.US, samplesPerPixel);
        dicom.putInt(Tag.Rows, VR.US, jpegImage.getHeight());
        dicom.putInt(Tag.Columns, VR.US, jpegImage.getWidth());
        dicom.putInt(Tag.BitsAllocated, VR.US, bitsAllocated);
        dicom.putInt(Tag.BitsStored, VR.US, bitsAllocated);
        dicom.putInt(Tag.HighBit, VR.US, bitsAllocated-1);
        dicom.putInt(Tag.PixelRepresentation, VR.US, PixelRepresentation);
        dicom.putString(Tag.LossyImageCompression, VR.CS, LossyImageCompression);

        // Build DICOM dataset:
        // Creation dateTime: parseDate
        dicom.putDate(Tag.InstanceCreationDate, VR.DA, new Date());
        dicom.putDate(Tag.InstanceCreationTime, VR.TM, new Date());
        
        // SOP Class UID creation:
        String SOPClassUID_value = device.get("TestUID");
        dicom.putString(Tag.SOPClassUID, VR.UI, SOPClassUID_value);  // 1.2.840.10008.5.1.4.1.1.12 doesn't exist!!!
        
        // Implementation Class UID creation:                  
            // Creating unique UID values.
            String root = "";
            if(device.get("DeviceUID").length() > 24){
                UIDUtils.setRoot("1");
                root = device.get("DeviceUID");
            }else{
                // Setting the value of the root.
                //UIDUtils.setRoot("1.2.724.0.75.2.1.20.1");  // 1:ISO, 2: branch of ISO, 724: Spain,
                UIDUtils.setRoot(device.get("DeviceUID")); // Open Source UID prefix 1.2.826.0.1.3680043.8.931 obtained from http://www.medicalconnections.co.uk/FreeUID.html.
            }
            System.out.println("getRoot: "+UIDUtils.getRoot());
            String SOPInstanceUID_value = root+UIDUtils.createUID();
            String StudyInstanceUID_value = study.get("StudyInstanceUID");  // It could have been taken from worklist. It corresponds to each study
            String SeriesInstanceUID_value = UIDUtils.createUID();


            // Testing Implementation Class UID values.
            int maxlength = 64;
            if(SOPInstanceUID_value.length() > maxlength){SOPInstanceUID_value = SOPInstanceUID_value.substring(0,maxlength);}
            if(StudyInstanceUID_value.length() > maxlength){StudyInstanceUID_value = StudyInstanceUID_value.substring(0,maxlength);}
            if(SeriesInstanceUID_value.length() > maxlength){SeriesInstanceUID_value = SeriesInstanceUID_value.substring(0,maxlength);}
            
            boolean isValid = UIDUtils.isValidUID(SOPInstanceUID_value);    // Not necessary.
            System.out.println("SOPInstanceUID isValid: "+isValid);

            // Adding UIDs to each attribute.            
            dicom.putString(Tag.SOPInstanceUID, VR.UI, SOPInstanceUID_value);
            dicom.putString(Tag.StudyInstanceUID, VR.UI, StudyInstanceUID_value);
            dicom.putString(Tag.SeriesInstanceUID, VR.UI, SeriesInstanceUID_value);            
            
            // There are no series in perimetry.            
            if(Laterality.equals("R")){SeriesNumber = "0";      
            }else if(Laterality.equals("L")){SeriesNumber = "1";}
            dicom.putString(Tag.SeriesNumber, VR.IS, SeriesNumber);      // Should be present with at least a zero-length.
            
        // Transfer syntax UID creation:
        dicom.initFileMetaInformation(ImageUID);

        // DICOM Attributes from DB:
            
            // Patient: [x]
            dicom.putString(Tag.PatientName, VR.PN, patient.get("PatientsSurName")+"^"+patient.get("PatientsName"));
            dicom.putString(Tag.PatientID, VR.LO, patient.get("PatientID"));
            dicom.putString(Tag.PatientBirthDate, VR.DA, patient.get("PatientsBirthDate").replaceAll("-", ""));
            dicom.putString(Tag.PatientSex, VR.CS, patient.get("PatientsSex"));
            dicom.putString(Tag.PatientComments, VR.LT, patient.get("PatientComments"));

            
            // Device:
            dicom.putString(Tag.Modality, VR.CS, device.get("Modality"));
            dicom.putString(Tag.Manufacturer, VR.LO, device.get("Manufacturer"));
            dicom.putString(Tag.InstitutionName, VR.LO, device.get("InstitutionName"));
            dicom.putString(Tag.InstitutionAddress, VR.ST, device.get("InstitutionAddress"));   // Add to database.
            dicom.putString(Tag.StationName, VR.SH, device.get("StationName"));
            dicom.putString(Tag.ManufacturerModelName, VR.LO, ManufacturersModelName);
            dicom.putString(Tag.DeviceSerialNumber, VR.LO, DeviceSerialNumber);
            //dicom.putString(Tag.PerformedStationName, VR.SH, device.get("StationName"));  // Repeated.
            dicom.putString(Tag.DeviceUID, VR.UI, device.get("DeviceUID"));
            dicom.putString(Tag.SoftwareVersions, VR.LO, SoftwareVersions);               // Add to database.
            dicom.putString(Tag.PerformedLocation, VR.SH, device.get("Location"));
            //dicom.putString(Tag.Location, VR.DS, device.get("Location"));         // RETIRED.
            
            /* Not yet defined in the standard.
            L1 = dicom.putSequence(Tag.AcquisitionDeviceTypeCodeSequence);
            CIDs = LL.getAcquisitionDeviceCID(device.get("AcquisitionDeviceTypeCodeSequence"));   // Gets AcquisitionDevice's SQ.
                dataset_L1 = new BasicDicomObject();
                    dataset_L1.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                    dataset_L1.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                    dataset_L1.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                    dataset_L1.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);*/
            dicom.putString(Tag.PerformedStationAETitle, VR.AE, device.get("AETitle"));
            
            
            // Worklist:
            dicom.putString(Tag.AccessionNumber, VR.SH, worklist.get("AccessionNumber"));
            dicom.putString(Tag.ReferringPhysicianName, VR.PN, worklist.get("RequestingPhysician"));

            //dicom.putString(Tag.RequestingPhysician, VR.PN, worklist.get("RequestingPhysician"));
            dicom.putString(Tag.RequestedProcedureDescription, VR.LO, worklist.get("RequestedProcedureDescription"));
            L1 = dicom.putSequence(Tag.RequestedProcedureCodeSequence);
                dataset_L1 = new BasicDicomObject();
                dataset_L1.putString(Tag.CodeMeaning, VR.LO, worklist.get("RequestedProcedureCodeSequence"));
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            L1 = dicom.putSequence(Tag.ScheduledProcedureStepSequence);
                dataset_L1 = new BasicDicomObject();
                dataset_L1.putString(Tag.ScheduledProcedureStepDescription, VR.LO, worklist.get("ScheduledProcedStepDescription"));
                L2 = dataset_L1.putSequence(Tag.ScheduledProtocolCodeSequence);
                    dataset_L2 = new BasicDicomObject();
                    dataset_L2.putString(Tag.CodeMeaning, VR.LO, worklist.get("ScheduledProcedStepProtocCode"));
                L2.addDicomObject(dataset_L2);
                dataset_L1.putString(Tag.ScheduledProcedureStepID, VR.SH, worklist.get("ScheduledProcedureStepID"));
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            dicom.putString(Tag.RequestedProcedureID, VR.SH, worklist.get("RequestedProcedureID"));
            dicom.putString(Tag.ScheduledProcedureStepStartDateTime, VR.DT, worklist.get("ScheduledProcedureStepDateTime"));
            
            
            // Study: DICOM General.
            dicom.putString(Tag.BodyPartExamined, VR.CS, BodyPartExamined);    // Fixed value.
             //dicom.putString(Tag.PatientPosition, VR.CS, PatientPosition);    // Fixed value.

            dicom.putString(Tag.StudyDate, VR.DA, study.get("StudyDate").replaceAll("-", ""));
            dicom.putString(Tag.StudyTime, VR.TM, StudyTime);      // Should be present with at least a zero-length.
            dicom.putString(Tag.StudyDescription, VR.LO, study.get("StudyDescription"));
            // --> Physician:   //dicom.putString(Tag.PerformingPhysicianIdentificationSequence, VR.UI, );
            dicom.putString(Tag.PerformingPhysicianName, VR.PN, physician.get("PerformingPhysiciansSurName")+"^"+physician.get("PerformingPhysiciansName"));
            dicom.putString(Tag.StudyID, VR.SH, study.get("StudyId"));
            dicom.putString(Tag.StudyInstanceUID, VR.UI, study.get("StudyInstanceUID"));            
            L1 = dicom.putSequence(dicom.resolveTag(0x00240112, "Refractive Parameters Used on Patient Sequence"));   // Perimetry specific attribute.
            //L1 = dicom.putSequence(Tag.RefractiveStateSequence);    // DICOM General attribute.
                dataset_L1 = new BasicDicomObject();
                    if(Laterality.equals("R")){
                        dataset_L1.putString(Tag.SphericalLensPower, VR.FL, study.get("SphericalLensPower_RightEye"));
                        dataset_L1.putString(Tag.CylinderLensPower, VR.FL, study.get("CylinderLensPower_RightEye"));
                        dataset_L1.putString(Tag.CylinderAxis, VR.FL, study.get("CylinderAxis_RightEye"));
                    }else if (Laterality.equals("L")){
                        dataset_L1.putString(Tag.SphericalLensPower, VR.FL, study.get("SphericalLensPower_LeftEye"));
                        dataset_L1.putString(Tag.CylinderLensPower, VR.FL, study.get("CylinderLensPower_LeftEye"));
                        dataset_L1.putString(Tag.CylinderAxis, VR.FL, study.get("CylinderAxis_LeftEye"));
                    }else{
                        System.out.println("Wrong laterality value: "+Laterality);
                        throw new MyException(MyException.SystemCallError,  Laterality);
                    }
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            
            // Study: Perimetry specific.
            L1 = dicom.putSequence(Tag.PerformedProtocolCodeSequence);            
                dataset_L1 = new BasicDicomObject();
                CIDs = LL.getTestTypeCID(study.get("TestpatternsCodeSequence"));   // Gets TestPattern's SQ.
                    /* // Not implemented in Database.
                    L2 = dataset_L1.putSequence(Tag.ProtocolContextSequence);
                    dataset_L2 = new BasicDicomObject();
                        CIDs = LL.getProtocolContextCID(study.get(""));   // Gets TestPattern's SQ.
                        dataset_L2.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                        dataset_L2.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                        dataset_L2.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                        dataset_L2.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
                    L2.addDicomObject(dataset_L2);*/
                    dataset_L1.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                    dataset_L1.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                    dataset_L1.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                    dataset_L1.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            dicom.putNestedDicomObject(Tag.PerformedProtocolCodeSequence, dataset_L1);                
                dataset_L1 = new BasicDicomObject();
                CIDs = LL.getStrategyCID(study.get("TestStrategyCodeSequence"));  // Gets TestStrategy's SQ.
                    /* // Not implemented in Database.
                    L2 = dataset_L1.putSequence(Tag.ProtocolContextSequence);
                    dataset_L2 = new BasicDicomObject();
                        CIDs = LL.getProtocolContextCID(study.get(""));   // Gets TestPattern's SQ.
                        dataset_L2.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                        dataset_L2.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                        dataset_L2.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                        dataset_L2.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
                    L2.addDicomObject(dataset_L2);*/
                    dataset_L1.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                    dataset_L1.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                    dataset_L1.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                    dataset_L1.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);

            L1 = dicom.putSequence(dicom.resolveTag(0x00240016, "Screening Test Mode Code Sequence"));   // Perimetry specific attribute.                          
                dataset_L1 = new BasicDicomObject();
                CIDs = LL.getTestModeCID(study.get("ScreeningTestModeCodeSequence"));  // Gets TestStrategy's SQ.
                    dataset_L1.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                    dataset_L1.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                    dataset_L1.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                    dataset_L1.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            
            dicom.putString(dicom.resolveTag(0x00240020, "Background Luminance"), VR.FL, study.get("BackgroundLuminance"));
            L1 = dicom.putSequence(dicom.resolveTag(0x00240021, "Stimulus Color Code Sequence"));
                dataset_L1 = new BasicDicomObject();
                CIDs = LL.getColorCID(study.get("StimulusColorCodeSequence"));  // Gets color's SQ.
                    dataset_L1.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                    dataset_L1.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                    dataset_L1.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                    dataset_L1.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            L1 = dicom.putSequence(dicom.resolveTag(0x00240024, "Background Illumination Color Code Sequence"));
                dataset_L1 = new BasicDicomObject();
                CIDs = LL.getColorCID(study.get("BackgroundIlluminationColorCodeSequence"));  // Gets color's SQ.
                    dataset_L1.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                    dataset_L1.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                    dataset_L1.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                    dataset_L1.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            dicom.putString(dicom.resolveTag(0x00240025, "Stimulus Area"), VR.FL, study.get("StimulusArea"));
            dicom.putString(Tag.StudyComments, VR.LT, study.get("StudyComments"));
            
            
            // Acquisition: DICOM General.
            L1 = dicom.putSequence(Tag.AcquisitionContextSequence);     // should be present with at least a zero-length.
            dicom.add(L1);

            dicom.putString(Tag.AcquisitionTime, VR.TM, acquisition.get("AcquisitionTime").replaceAll(":", "")+".0");
            dicom.putString(Tag.AcquisitionNumber, VR.IS, acquisition.get("Id"));  // Repeated.
            dicom.putString(Tag.InstanceNumber, VR.IS, acquisition.get("Id"));      // Should be present with at least a zero-length.
            dicom.putString(Tag.PatientOrientation, VR.CS, PatientOrientation);     // Should be present with at least a zero-length.
            dicom.putString(Tag.ImagesInAcquisition, VR.IS, ImagesInAcquisition);   // Should be present with at least a zero-length.
            dicom.putString(Tag.Laterality, VR.CS, Laterality);         // Used when the images are taken from the same eye in a serie: if there are instances from different eyes, must be absent.
            dicom.putString(Tag.ImageLaterality, VR.CS, Laterality);    // Repeated.
            dicom.putString(dicom.resolveTag(0x00240113, "Measurement Laterality"), VR.CS, Laterality);  // Used when the images are taken from different eyes.

            // Acquisition: Perimetry specific.
            L1 = dicom.putSequence(dicom.resolveTag(0x00240032, "Fixation Sequence"));
                dataset_L1 = new BasicDicomObject();
                    L2 = dataset_L1.putSequence(dicom.resolveTag(0x00240033,"Fixation Monitoring Code Sequence"));
                        dataset_L2 = new BasicDicomObject();
                        CIDs = LL.getFixationMonitorCID(study.get("FixationMonitoringCodeSequence"));  // Gets TestStrategy's SQ.
                        dataset_L2.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                        dataset_L2.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                        dataset_L2.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                        dataset_L2.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
                    L2.addDicomObject(dataset_L2);
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            dicom.putNestedDicomObject(dicom.resolveTag(0x00240032, "Fixation Sequence"), dataset_L1);
                dataset_L1 = new BasicDicomObject();
                dataset_L1.putString(dicom.resolveTag(0x00240035, "Fixation Checked Quantity"), VR.US, acquisition.get("FixationCheckedQuantity"));
                dataset_L1.putString(dicom.resolveTag(0x00240036, "Patient Not Properly Fixated Quantity"), VR.US, acquisition.get("PatientNotProperlyFixatedQuantity"));
            L1.addDicomObject(dataset_L1);            
            dicom.add(L1);
            
            dicom.putString(dicom.resolveTag(0x00240086, "Foveal Sensitivity Measured"), VR.CS, acquisition.get("FovealSensitivityMeasured"));
            dicom.putString(dicom.resolveTag(0x00240087, "Foveal Sensitivity"), VR.FL, acquisition.get("FovealSensitivity"));
                       
            L1 = dicom.putSequence(dicom.resolveTag(0x00240034, "Visual Field Catch Trial Sequence"));
                dataset_L1 = new BasicDicomObject();
                if(!acquisition.get("FalseNegativesEstimate").isEmpty()){   // COULD BE NULL?
                    dataset_L1.putString(dicom.resolveTag(0x00240045, "False Negatives Estimate Flag"), VR.CS, "YES");
                    dataset_L1.putString(dicom.resolveTag(0x00240046, "False Negatives Estimate"), VR.FL, acquisition.get("FalseNegativesEstimate"));
                }else{
                    dataset_L1.putString(dicom.resolveTag(0x00240045, "False Negatives Estimate Flag"), VR.CS, "NO");
                }
                if(!acquisition.get("FalsePositivesEstimate").isEmpty()){
                    dataset_L1.putString(dicom.resolveTag(0x00240053,"False Positives Estimate Flag"), VR.CS, "YES");
                    dataset_L1.putString(dicom.resolveTag(0x00240054,"False Positives Estimate"), VR.FL, acquisition.get("FalsePositivesEstimate"));
                }else{
                    dataset_L1.putString(dicom.resolveTag(0x00240053,"False Positives Estimate Flag"), VR.CS, "NO");
                }
            L1.addDicomObject(dataset_L1);
            dicom.add(L1);
            String[] VisualFieldTestDuration = acquisition.get("VisualFieldTestDuration").split(":");
            String VisualFieldTestDuration_result = String.valueOf(Integer.parseInt(VisualFieldTestDuration[0])*3600+Integer.parseInt(VisualFieldTestDuration[1])*60+Integer.parseInt(VisualFieldTestDuration[2]));
            System.out.println("Hr: "+VisualFieldTestDuration[0]+" Min: "+VisualFieldTestDuration[1]+" Sec: "+VisualFieldTestDuration[2]+" Result: "+VisualFieldTestDuration_result);
            dicom.putString(dicom.resolveTag(0x00240088, "Visual Field Test Duration"), VR.FL, VisualFieldTestDuration_result);
            
            if(acquisition.get("GlobalDeviationFromNormal").isEmpty() || acquisition.get("LocalizedDeviationFromNormal").isEmpty()){ // In our case it will be always filled.
                dicom.putString(dicom.resolveTag(0x00240063, "Visual Field Test Normals Flag"), VR.CS, "NO");    // Add to database!!!
            }else{           // Add all sub-attributes related to Visual Field Test Normals.                
                dicom.putString(dicom.resolveTag(0x00240063, "Visual Field Test Normals Flag"), VR.CS, "YES");                
                L1 = dicom.putSequence(dicom.resolveTag(0x00240064, "Results Normals Sequence"));
                    dataset_L1 = new BasicDicomObject();
                    dataset_L1.putString(dicom.resolveTag(0x00240066,"Global Deviation from Normal"), VR.FL, acquisition.get("GlobalDeviationFromNormal"));
                    dataset_L1.putString(dicom.resolveTag(0x00240059,"Global Deviation Probability Normals Flag"), VR.CS, acquisition.get("GlobalDeviationProbabilityNormalsExist"));
                    //dataset_L1.putString(dicom.resolveTag(0x00240083,"Global Deviation Probability Sequence"), VR.SQ, acquisition.get(""));
                    L2 = dataset_L1.putSequence(dicom.resolveTag(0x00240083,"Global Deviation Probability Sequence"));
                        dataset_L2 = new BasicDicomObject();
                        dataset_L2.putString(dicom.resolveTag(0x00240071,"Global Deviation Probability"), VR.FL, acquisition.get("GlobalDeviationProbability"));
                    L2.addDicomObject(dataset_L2);
                L1.addDicomObject(dataset_L1);
                dicom.add(L1);
                dicom.putNestedDicomObject(dicom.resolveTag(0x00240064, "Results Normals Sequence"), dataset_L1);
                    dataset_L1 = new BasicDicomObject();
                    dataset_L1.putString(dicom.resolveTag(0x00240068,"Localized Deviation from Normal"), VR.FL, acquisition.get("LocalizedDeviationFromNormal"));
                    dataset_L1.putString(dicom.resolveTag(0x00240072,"Local Deviation Probability Normals Flag"), VR.CS, acquisition.get("LocalDeviationProbabilityNormalsExist"));
                    //dataset_L1.putString(dicom.resolveTag(0x00240085,"Localized Deviation Probability Sequence"), VR.SQ, acquisition.get(""));
                    L2 = dataset_L1.putSequence(dicom.resolveTag(0x00240085,"Localized Deviation Probability Sequence"));
                        dataset_L2 = new BasicDicomObject();
                        dataset_L2.putString(dicom.resolveTag(0x00240073,"Localized Deviation Probability"), VR.FL, acquisition.get("LocalizedDeviationProbability"));
                    L2.addDicomObject(dataset_L2);
                L1.addDicomObject(dataset_L1);
                dicom.add(L1);
            }
            dicom.putString(dicom.resolveTag(0x00240074,"Short Term Fluctuation Calculated"), VR.CS, acquisition.get("ShortTermFluctuationCalculated"));
            dicom.putString(dicom.resolveTag(0x00240075,"Short Term Fluctuation"), VR.FL, acquisition.get("ShortTermFluctuation"));
            //dicom.putString(dicom.resolveTag(0x00240076,"Short Term Fluctuation Probability Calculated"), VR.CS, acquisition.get(""));    // Not implemented on the device nor database.
            //dicom.putString(dicom.resolveTag(0x00240077,"Short Term Fluctuation Probability"), VR.FL, acquisition.get(""));               // Not implemented on the device nor database.
            dicom.putString(dicom.resolveTag(0x00240078,"Corrected Localized Deviation From Normal Calculated"), VR.CS, acquisition.get("CorrectedLocalizedDeviationFromNormalCalculated"));
            dicom.putString(dicom.resolveTag(0x00240079,"Corrected Localized Deviation From Normal"), VR.FL, acquisition.get("CorrectedLocalizedDeviationFromNormal"));
            //dicom.putString(dicom.resolveTag(0x00240080,"Corrected Localized Deviation From Normal Probability Calculated"), VR.CS, acquisition.get("")); // Not implemented on the device nor database.
            //dicom.putString(dicom.resolveTag(0x00240081,"Corrected Localized Deviation From Normal Probability"), VR.FL, acquisition.get(""));            // Not implemented on the device nor database.
            
            L1 = dicom.putSequence(dicom.resolveTag(0x00240325, "Data Observation Sequence"));
                dataset_L1 = new BasicDicomObject();
                dataset_L1.putString(Tag.ValueType, VR.CS, ValueType); // It will be predefined as code.
                L2 = dataset_L1.putSequence(Tag.ConceptNameCodeSequence);                
                    dataset_L2 = new BasicDicomObject();
                    CIDs = LL.getConceptNameCID(acquisition.get("GlobalIndexName"));
                    dataset_L2.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                    dataset_L2.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                    dataset_L2.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                    dataset_L2.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
                 L2.addDicomObject(dataset_L2);
            L1.addDicomObject(dataset_L1);  
            dicom.add(L1);
            System.out.println("GlobalIndexName: "+acquisition.get("GlobalIndexName"));
            dicom.putNestedDicomObject(dicom.resolveTag(0x00240325, "Data Observation Sequence"),dataset_L1);
                dataset_L1 = new BasicDicomObject();
                L2 = dataset_L1.putSequence(Tag.ConceptCodeSequence);                   
                dataset_L2 = new BasicDicomObject();     
                    CIDs = LL.getConceptCID(acquisition.get("GlaucomaHemifieldTestAnalysisCodeSequence"));
                    dataset_L2.putString(Tag.CodingSchemeDesignator, VR.SH, CIDs.get(0));
                    dataset_L2.putString(Tag.CodeValue, VR.SH, CIDs.get(1));
                    dataset_L2.putString(Tag.CodeMeaning, VR.LO, CIDs.get(2));
                    dataset_L2.putString(Tag.ContextUID, VR.UI, CIDs.get(3));
                L2.addDicomObject(dataset_L2);   
            L1.addDicomObject(dataset_L1);  
            dicom.add(L1);
            System.out.println("GlaucomaHemifieldTestAnalysisCodeSequence: "+acquisition.get("GlaucomaHemifieldTestAnalysisCodeSequence"));
            /*dicom.putNestedDicomObject(dicom.resolveTag(0x00240325, "Data Observation Sequence"),dataset_L1);
                dataset_L1 = new BasicDicomObject();
                dataset_L1.putString(Tag.NumericValue, VR.DS, acquisition.get(""));
            L1.addDicomObject(dataset_L1); 
            dicom.add(L1);*/
            
            // End of DICOM attribute tables.
            
        // Create dicom physical file:         
        FileOutputStream fos = new FileOutputStream(newFileDCM);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        DicomOutputStream dos = new DicomOutputStream(bos);
        dicom.putString(Tag.ImplementationVersionName, VR.SH, ImplementationVersionName);   // Add our own implementation version name instead of dcm4che2.
        
        dos.writeDicomFile(dicom);

        // Write pixel data:
        dos.writeHeader(Tag.PixelData, VR.OB, -1);
        dos.writeHeader(Tag.Item, null, 0);

        // Writes the lenght of the JPEG file (always an even lenght):
        int jpgLen = (int) jpgSource.length();
        dos.writeHeader(Tag.Item, null, (jpgLen+1)&~1);

        // Start writing:
        FileInputStream fis = new FileInputStream(jpgSource);
        BufferedInputStream bis = new BufferedInputStream(fis);
        DataInputStream dis = new DataInputStream(bis);

        byte[] buffer = new byte[65536];
        int b;
        while ((b = dis.read(buffer)) > 0) {
            dos.write(buffer, 0, b);
        }

        // Write over the dicom file:
        if ((jpgLen&1) != 0) dos.write(0);
            dos.writeHeader(Tag.SequenceDelimitationItem, null, 0);
            dos.close();
        
    }
}
